#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "constants.h"
#include "tool/stringtinylib.h"
#include "tool/linklistlib.h"
#include "instruction.h"
#include "function.h"
#include <time.h>
#include <math.h>

typedef struct RuntimeStack{
	Op * pElmnts;
	int iSize;
	int iTopIndex;
	int iFrameIndex;
}RuntimeStack;

typedef struct InstrStream {
    Instr * pInstrs;
    int iSize;
    int iCurrInstr;
} InstrStream;

typedef struct HostAPICallTable {
	char ** ppstrCalls;
	int iSize;
} HostAPICallTable;

typedef struct Script {
	int iGlobalDataSize;
	int iIsMainFuncPresent;
	int iMainFuncIndex;
	int iIsPaused;
	long iPauseEndTime;
	Op RetVal;
	InstrStream InstrStream;
	RuntimeStack Stack;
	FuncNode * pFuncTable;
	HostAPICallTable HostAPICallTable;
} Script;

Script g_Script;
char ppstrMnemonics [][ 12 ] ={
	"Mov",
	"Add", "Sub", "Mul", "Div", "Mod", "Exp", "Neg", "Inc", "Dec",
	"And", "Or", "XOr", "Not", "ShL", "ShR",
	"Concat", "GetChar", "SetChar",
	"Jmp", "JE", "JNE", "JG", "JL", "JGE", "JLE",
	"Push", "Pop",
	"Call", "Ret", "CallHost",
	"Pause", "Exit"
};

#define ResolveStackIndex( iIndex )		( iIndex < 0 ? iIndex += g_Script.Stack.iFrameIndex : iIndex )

void Init ();
void ShutDown ();
void ResetScript ();
void PushFrame ( int iSize );
void PopFrame ( int iSize );
void CopyOp ( Op * pDest, Op Source );
int CoerceOpToInt ( Op Val );
float CoerceOpToFloat( Op Val );
char * CoerceOpToString ( Op Val );
int GetOpType ( int iOpIndex );
Op GetStackOp( int iIndex );
int ResolveOpStackIndex ( int iOpIndex );
Op ResolveOp( int iOpIndex );
int ResolveOpType ( int iOpIndex );
int ResolveOpAsInt ( int iOpIndex );
float ResolveOpAsFloat ( int iOpIndex );
char * ResolveOpAsString ( int iOpIndex );
int ResolveOpIValue(int iOpIndex);
char * GetHostAPICall ( int iIndex );
char * ResolveOpAsHostAPICall ( int iOpIndex );
Op * ResolveOpPntr ( int iOpIndex );
void Push ( Op Val );
Op Pop ();
FuncNode GetFunc ( int iIndex );
void PrintOpIndir ( int iOpIndex );
void PrintOpValue ( int iOpIndex );
int LoadScript ( char * pstrFilename );
void RunScript ();
unsigned long GetCurrTime();
void WaitForInput();

int main( int argc, char * argv [] ){

	if( argc < 2){
		printf("require a input file\n");
		return 0;
	}

	Init ();

	WaitForInput();

	int iErrorCode = LoadScript ( argv[1] );
	if ( iErrorCode != LOAD_OK ){
		printf ( "Error: " );
		switch ( iErrorCode ){
			case LOAD_ERROR_FILE_IO:
				printf ( "File I/O error" );
				break;
			case LOAD_ERROR_INVALID_XSE:
				printf ( ".XSE structure invalid" );
				break;
			case LOAD_ERROR_UNSUPPORTED_VERS:
				printf ( "Unsupported .XSE format version" );
				break;
		}
		printf ( ".\n" );
		return 0;
	}

	ResetScript ();
	RunScript ();
	ShutDown ();
	return 0;
}

void Init(){
	g_Script.iIsMainFuncPresent = FALSE;
	g_Script.iIsPaused = FALSE;
	
	g_Script.InstrStream.pInstrs = NULL;
	g_Script.Stack.pElmnts = NULL;
	g_Script.pFuncTable = NULL;
	g_Script.HostAPICallTable.ppstrCalls = NULL;
}

void ShutDown (){
	int iCurrInstrIndex = 0;
	for( ; iCurrInstrIndex < g_Script.InstrStream.iSize; ++ iCurrInstrIndex ){
		int iOpCount = g_Script.InstrStream.pInstrs [ iCurrInstrIndex ].iOpCount;
		Op * pOpList = g_Script.InstrStream.pInstrs [ iCurrInstrIndex ].pOpList;

		int iCurrOpIndex = 0;
		for( ; iCurrOpIndex < iOpCount; ++ iCurrOpIndex )
			if ( pOpList[iCurrOpIndex].iType==OP_TYPE_STRING )
				free (pOpList[iCurrOpIndex].pValue);
	}

	if ( g_Script.InstrStream.pInstrs )
		free ( g_Script.InstrStream.pInstrs );

	int iCurrElmtnIndex = 0;
	for( ; iCurrElmtnIndex < g_Script.Stack.iSize; ++ iCurrElmtnIndex )
		if ( g_Script.Stack.pElmnts [ iCurrElmtnIndex ].iType == OP_TYPE_STRING )
			free( g_Script.Stack.pElmnts [ iCurrElmtnIndex ].pValue );

	if ( g_Script.Stack.pElmnts )
		free ( g_Script.Stack.pElmnts );

	if ( g_Script.pFuncTable )
		free ( g_Script.pFuncTable );

	int iCurrCallIndex = 0;
	for(; iCurrCallIndex < g_Script.HostAPICallTable.iSize; ++ iCurrCallIndex )
		if ( g_Script.HostAPICallTable.ppstrCalls &&  g_Script.HostAPICallTable.ppstrCalls[iCurrCallIndex] )
			free ( g_Script.HostAPICallTable.ppstrCalls [ iCurrCallIndex ] );

	if ( g_Script.HostAPICallTable.ppstrCalls )
		free( g_Script.HostAPICallTable.ppstrCalls );
		
}

void ResetScript (){
	int iMainFuncIndex = g_Script.iMainFuncIndex;

	if ( g_Script.pFuncTable ){
		if ( g_Script.iIsMainFuncPresent ){
			g_Script.InstrStream.iCurrInstr = g_Script.pFuncTable [ iMainFuncIndex ].iEntryPoint;
		}
	}

	g_Script.Stack.iTopIndex = 0;
	g_Script.Stack.iFrameIndex = 0;

	int iCurrElmntIndex = 0;
	for( ; iCurrElmntIndex < g_Script.Stack.iSize; ++ iCurrElmntIndex )
		g_Script.Stack.pElmnts [ iCurrElmntIndex ].iType = OP_TYPE_NULL;

	g_Script.iIsPaused = FALSE;
	PushFrame ( g_Script.iGlobalDataSize );

	int iStackFrameSize = g_Script.pFuncTable [ iMainFuncIndex ].iParamCount + g_Script.pFuncTable [ iMainFuncIndex ].iLocalDataSize + 1;
	PushFrame ( iStackFrameSize + 1 );
}

void PushFrame ( int iSize ){
	g_Script.Stack.iTopIndex += iSize;
	g_Script.Stack.iFrameIndex = g_Script.Stack.iTopIndex;
}

void PopFrame ( int iSize ){
	g_Script.Stack.iTopIndex -= iSize;
}

void CopyOp ( Op * pDest, Op Source ){
	if ( pDest->iType == OP_TYPE_STRING )
		free ( pDest->pValue );

	* pDest = Source;
	if( Source.iType == OP_TYPE_STRING ){
		pDest->pValue = (char *) malloc( strlen(Source.pValue)+1 );
		strcpy(pDest->pValue, Source.pValue);
	}
}

int CoerceOpToInt ( Op Val ){
	switch (Val.iType){
		case OP_TYPE_INT:
			return Val.iValue;
		case OP_TYPE_FLOAT:
			return (int)Val.fValue;
		case OP_TYPE_STRING:
			return atoi(Val.pValue);
		default:
			return 0;
	}
}

float CoerceOpToFloat( Op Val ){
	switch (Val.iType){
                case OP_TYPE_INT:
                        return (float)Val.iValue;
                case OP_TYPE_FLOAT:
                        return Val.fValue;
                case OP_TYPE_STRING:
                        return (float)atof(Val.pValue);
                default:
                        return 0;
        }
}

char * CoerceOpToString ( Op Val ){
	char * pstrCoercion;
	if ( Val.iType != OP_TYPE_STRING )
		pstrCoercion = (char *)malloc( MAX_COERCION_STRING_SIZE + 1 );

	switch ( Val.iType ){
		case OP_TYPE_INT:
			//itoa ( Val.iValue, pstrCoercion, 10 );
			sprintf( pstrCoercion, "%d", Val.iValue);
                        return pstrCoercion;
                case OP_TYPE_FLOAT:
			sprintf( pstrCoercion, "%f", Val.fValue);
                        return pstrCoercion;
                case OP_TYPE_STRING:
                        return Val.pValue;
                default:
                        return NULL;
	}
}

int GetOpType ( int iOpIndex ){
	int iCurrInstr = g_Script.InstrStream.iCurrInstr;
	return g_Script.InstrStream.pInstrs [ iCurrInstr ].pOpList [ iOpIndex ].iType;
}

Op GetStackOp( int iIndex ){
	return g_Script.Stack.pElmnts[ ResolveStackIndex( iIndex ) ];
}

void SetStackOp ( int iIndex, Op Val ){
	g_Script.Stack.pElmnts [ ResolveStackIndex ( iIndex ) ] = Val;
}

int ResolveOpStackIndex ( int iOpIndex ){
	int iCurrInstr = g_Script.InstrStream.iCurrInstr;
	Op OpValue = g_Script.InstrStream.pInstrs [ iCurrInstr ].pOpList [ iOpIndex ];
	switch ( OpValue.iType ){
		case OP_TYPE_ABS_STACK_INDEX:
			return OpValue.iValue;

		case OP_TYPE_REL_STACK_INDEX:{
			int iBaseIndex = OpValue.iValue;
			int iOffsetIndex = OpValue.iOffsetIndex;

			Op StackOp = GetStackOp ( iOffsetIndex );
			if ( iBaseIndex >= 0) // global
				return iBaseIndex + StackOp.iValue;
			else // local
				return iBaseIndex - StackOp.iValue;
		}

		default:
			return 0;
	}
}

Op ResolveOp( int iOpIndex ){
	int iCurrInstr = g_Script.InstrStream.iCurrInstr;
	Op OpValue = g_Script.InstrStream.pInstrs [ iCurrInstr ].pOpList [ iOpIndex ];

	switch ( OpValue.iType ){
		case OP_TYPE_ABS_STACK_INDEX:
		case OP_TYPE_REL_STACK_INDEX:
		{
			int iAbsIndex = ResolveOpStackIndex ( iOpIndex );
			return GetStackOp ( iAbsIndex );
		}

		case OP_TYPE_REG:
			return g_Script.RetVal;

		default:
			return OpValue;
	}
}

int ResolveOpType ( int iOpIndex ){
	Op OpValue = ResolveOp ( iOpIndex );
	return OpValue.iType;
}

int ResolveOpAsInt ( int iOpIndex ){
	Op OpValue = ResolveOp ( iOpIndex );
	return CoerceOpToInt ( OpValue );
}

float ResolveOpAsFloat ( int iOpIndex ){
	Op OpValue = ResolveOp ( iOpIndex );
	return CoerceOpToFloat ( OpValue );
}

char * ResolveOpAsString ( int iOpIndex ){
	Op OpValue = ResolveOp ( iOpIndex );
	return CoerceOpToString ( OpValue );
}

int ResolveOpIValue(int iOpIndex){
	Op OpValue = ResolveOp ( iOpIndex );
	return OpValue.iValue;
}

char * GetHostAPICall ( int iIndex ){
	return g_Script.HostAPICallTable.ppstrCalls [ iIndex ];
}

char * ResolveOpAsHostAPICall ( int iOpIndex ){
	int iHostAPICallIndex = ResolveOpIValue( iOpIndex );
	return GetHostAPICall ( iHostAPICallIndex );
}

Op * ResolveOpPntr ( int iOpIndex ){
	int iIndirMethod = GetOpType ( iOpIndex );
	switch ( iIndirMethod ){
		case OP_TYPE_REG:
			return & g_Script.RetVal;

		case OP_TYPE_ABS_STACK_INDEX:
		case OP_TYPE_REL_STACK_INDEX:
		{
			int iStackIndex = ResolveOpStackIndex ( iOpIndex );
			return & g_Script.Stack.pElmnts [ ResolveStackIndex ( iStackIndex ) ];
		}
	}

	return NULL;
}

void Push ( Op Val ){
	g_Script.Stack.pElmnts [ g_Script.Stack.iTopIndex ] = Val;
	++ g_Script.Stack.iTopIndex;
}

Op Pop (){
	-- g_Script.Stack.iTopIndex;
	Op Val;
	CopyOp ( & Val, g_Script.Stack.pElmnts [ g_Script.Stack.iTopIndex ] );

	return Val;
}

FuncNode GetFunc ( int iIndex ){
	return g_Script.pFuncTable [ iIndex ];
}

unsigned long GetCurrTime(){
	struct timespec ts;
	clock_gettime(CLOCK_MONOTONIC, &ts);
	return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000);
}

void WaitForInput(){
	printf("Press Any Key To Continue ......\n");
	char c;
	scanf("%c", &c);
}

void PrintOpIndir ( int iOpIndex ){
	int iIndirMethod = GetOpType ( iOpIndex );
	switch ( iIndirMethod ){
		case OP_TYPE_REG:
			printf ( "RetVal" );
			break;

		case OP_TYPE_ABS_STACK_INDEX:
		case OP_TYPE_REL_STACK_INDEX:
		{
			int iStackIndex = ResolveOpStackIndex ( iOpIndex );
			printf ( "[ %d ]", iStackIndex );
			break;
		}
	}
}

void PrintOpValue ( int iOpIndex ){
	Op Op  = ResolveOp ( iOpIndex );

	switch ( Op.iType ){
        	case OP_TYPE_NULL:
			printf ( "Null" );
			break;

		case OP_TYPE_FLOAT:
			printf ( "%f", Op.fValue );
			break;

		case OP_TYPE_STRING:
			printf ( "\"%s\"", Op.pValue );
			break;

		case OP_TYPE_INT:
		case OP_TYPE_INSTR_INDEX:
			printf ( "%d", Op.iValue );
			break;

		case OP_TYPE_HOST_API_CALL_INDEX:
		{
			char * pstrHostAPICall = ResolveOpAsHostAPICall ( iOpIndex );
			printf ( "Host API Call: %s", pstrHostAPICall );
			break;
		}
	}
}

int LoadScript ( char * pstrFilename ){
	FILE * pScriptFile;
	if ( ! ( pScriptFile = fopen ( pstrFilename, "rb" ) ) )
		return LOAD_ERROR_FILE_IO;

	char * pstrIDString = ( char * ) malloc ( 5 );
	fread ( pstrIDString, 4, 1, pScriptFile );
	pstrIDString [ strlen ( XSE_ID_STRING ) ] = '\0';
	if ( strcmp ( pstrIDString, XSE_ID_STRING ) != 0 )
		return LOAD_ERROR_INVALID_XSE;
	free ( pstrIDString );

	int iMajorVersion = 0, iMinorVersion = 0;
	fread ( & iMajorVersion, 1, 1, pScriptFile );
	fread ( & iMinorVersion, 1, 1, pScriptFile );
	if ( iMajorVersion != 1 || iMinorVersion != 0 )
		return LOAD_ERROR_UNSUPPORTED_VERS;

	fread ( & g_Script.Stack.iSize, 4, 1, pScriptFile );
	if ( g_Script.Stack.iSize == 0 )
		g_Script.Stack.iSize = DEF_STACK_SIZE;
	 g_Script.Stack.pElmnts = (Op *)malloc ( g_Script.Stack.iSize * sizeof ( Op ) );

	fread ( & g_Script.iGlobalDataSize, 4, 1, pScriptFile );
	fread ( & g_Script.iIsMainFuncPresent, 1, 1, pScriptFile );
	fread ( & g_Script.iMainFuncIndex, 4, 1, pScriptFile );
	fread ( & g_Script.InstrStream.iSize, 4, 1, pScriptFile );

	g_Script.InstrStream.pInstrs = ( Instr * ) malloc ( g_Script.InstrStream.iSize * sizeof ( Instr ) );

	int iCurrInstrIndex = 0;
	for( ; iCurrInstrIndex < g_Script.InstrStream.iSize; ++ iCurrInstrIndex ){
		g_Script.InstrStream.pInstrs [ iCurrInstrIndex ].iOpcode = 0;
		fread ( & g_Script.InstrStream.pInstrs [ iCurrInstrIndex ].iOpcode, 2, 1, pScriptFile );

		g_Script.InstrStream.pInstrs [ iCurrInstrIndex ].iOpCount = 0;
		fread ( & g_Script.InstrStream.pInstrs [ iCurrInstrIndex ].iOpCount, 1, 1, pScriptFile );

		int iOpCount = g_Script.InstrStream.pInstrs [ iCurrInstrIndex ].iOpCount;
		Op * pOpList = (Op *) malloc ( iOpCount * sizeof ( Op ) );

		int iCurrOpIndex = 0;
		for (; iCurrOpIndex < iOpCount; ++ iCurrOpIndex ){
			pOpList [ iCurrOpIndex ].iType = 0;
			fread ( & pOpList [ iCurrOpIndex ].iType, 1, 1, pScriptFile );

			switch ( pOpList [ iCurrOpIndex ].iType ){
				case OP_TYPE_INT:
					fread ( & pOpList [ iCurrOpIndex ].iValue, sizeof ( int ), 1, pScriptFile );
					break;

				case OP_TYPE_FLOAT:
					fread ( & pOpList [ iCurrOpIndex ].fValue, sizeof ( float ), 1, pScriptFile );
					break;

				case OP_TYPE_STRING:
					fread ( & pOpList [ iCurrOpIndex ].iValue, sizeof ( int ), 1, pScriptFile );
					break;

				case OP_TYPE_INSTR_INDEX:
					fread ( & pOpList [ iCurrOpIndex ].iValue, sizeof ( int ), 1, pScriptFile );
					break;

				case OP_TYPE_ABS_STACK_INDEX:
					fread ( & pOpList [ iCurrOpIndex ].iValue, sizeof ( int ), 1, pScriptFile );
					break;

				case OP_TYPE_REL_STACK_INDEX:
					fread ( & pOpList [ iCurrOpIndex ].iValue, sizeof ( int ), 1, pScriptFile );
					fread ( & pOpList [ iCurrOpIndex ].iOffsetIndex, sizeof ( int ), 1, pScriptFile );
					break;

				case OP_TYPE_FUNC_INDEX:
					fread ( & pOpList [ iCurrOpIndex ].iValue, sizeof ( int ), 1, pScriptFile );
					break;

				case OP_TYPE_HOST_API_CALL_INDEX:
					fread ( & pOpList [ iCurrOpIndex ].iValue, sizeof ( int ), 1, pScriptFile );
					break;

				case OP_TYPE_REG:
					fread ( & pOpList [ iCurrOpIndex ].iValue, sizeof ( int ), 1, pScriptFile );
					break;
			}
		}
		g_Script.InstrStream.pInstrs [ iCurrInstrIndex ].pOpList = pOpList;
	}

	int iStringTableSize;
	fread ( & iStringTableSize, 4, 1, pScriptFile );

	if( iStringTableSize > 0 ){
		char ** ppstrStringTable = ( char ** ) malloc ( iStringTableSize * sizeof ( char * ) );

		int iCurrStringIndex = 0;
		for( ; iCurrStringIndex < iStringTableSize; ++ iCurrStringIndex ){
			int iStringSize;
			fread ( & iStringSize, 4, 1, pScriptFile );
			char * pstrCurrString = ( char * ) malloc ( iStringSize + 1 );
			fread ( pstrCurrString, iStringSize, 1, pScriptFile );
			pstrCurrString [ iStringSize ] = '\0';
			ppstrStringTable [ iCurrStringIndex ] = pstrCurrString;
		}

		int iCurrInstrIndex = 0;
		for( ; iCurrInstrIndex < g_Script.InstrStream.iSize; ++ iCurrInstrIndex ){
			int iOpCount = g_Script.InstrStream.pInstrs [ iCurrInstrIndex ].iOpCount;
			Op * pOpList = g_Script.InstrStream.pInstrs [ iCurrInstrIndex ].pOpList;

			int iCurrOpIndex = 0;
			for( ; iCurrOpIndex < iOpCount; ++ iCurrOpIndex ){
				if ( pOpList [ iCurrOpIndex ].iType == OP_TYPE_STRING ){
					int iStringIndex = pOpList [ iCurrOpIndex ].iValue;
					char * pstrStringCopy = ( char * ) malloc ( strlen ( ppstrStringTable [ iStringIndex ] ) + 1 );
					strcpy ( pstrStringCopy, ppstrStringTable [ iStringIndex ] );
					pOpList [ iCurrOpIndex ].pValue = pstrStringCopy;
				}
			}
		}

		for ( iCurrStringIndex = 0; iCurrStringIndex < iStringTableSize; ++ iCurrStringIndex )
			free ( ppstrStringTable [ iCurrStringIndex ] );

		free ( ppstrStringTable );
	}

	int iFuncTableSize;
	fread ( & iFuncTableSize, 4, 1, pScriptFile );
	g_Script.pFuncTable = ( FuncNode * ) malloc ( iFuncTableSize * sizeof ( FuncNode ) );

	int iCurrFuncIndex = 0;
	for(; iCurrFuncIndex < iFuncTableSize; ++ iCurrFuncIndex ){
		int iEntryPoint;
		fread ( & iEntryPoint, 4, 1, pScriptFile );
		int iParamCount = 0;
		fread ( & iParamCount, 1, 1, pScriptFile );
		int iLocalDataSize;
		fread ( & iLocalDataSize, 4, 1, pScriptFile );

		g_Script.pFuncTable [ iCurrFuncIndex ].iEntryPoint = iEntryPoint;
		g_Script.pFuncTable [ iCurrFuncIndex ].iParamCount = iParamCount;
		g_Script.pFuncTable [ iCurrFuncIndex ].iLocalDataSize = iLocalDataSize;
	}

	fread ( & g_Script.HostAPICallTable.iSize, 4, 1, pScriptFile );
	g_Script.HostAPICallTable.ppstrCalls = ( char ** ) malloc ( g_Script.HostAPICallTable.iSize * sizeof ( char * ) );

	int iCurrCallIndex = 0;
	for( ; iCurrCallIndex < g_Script.HostAPICallTable.iSize; ++ iCurrCallIndex ){
		int iCallLength = 0;
		fread ( & iCallLength, 1, 1, pScriptFile );
		char * pstrCurrCall = ( char * ) malloc ( iCallLength + 1 );
		fread ( pstrCurrCall, iCallLength, 1, pScriptFile );
		pstrCurrCall [ iCallLength ] = '\0';

		g_Script.HostAPICallTable.ppstrCalls [ iCurrCallIndex ] = pstrCurrCall;
	}

	fclose ( pScriptFile );

	printf ( "%s loaded successfully!\n", pstrFilename );
	printf ( "  Format Version: %d.%d\n", iMajorVersion, iMinorVersion );
	printf ( "      Stack Size: %d\n", g_Script.Stack.iSize );
	printf ( "Global Data Size: %d\n", g_Script.iGlobalDataSize );
	printf ( "       Functions: %d\n", iFuncTableSize );
	printf ( "_Main () Present: " );
	if ( g_Script.iIsMainFuncPresent )
		printf ( "Yes (Index %d)", g_Script.iMainFuncIndex );
	else
		printf ( "No" );
	printf ( "\n" );
	printf ( "  Host API Calls: %d\n", g_Script.HostAPICallTable.iSize );
	printf ( "    Instructions: %d\n", g_Script.InstrStream.iSize );
	printf ( " String Literals: %d\n", iStringTableSize );
	printf ( "\n" );

	return LOAD_OK;
}

void RunScript (){
	int iExitExecLoop = FALSE;

	while(TRUE){

	if ( g_Script.iIsPaused ){
		if ( GetCurrTime () <= g_Script.iPauseEndTime ){
			continue;
		}
		g_Script.iIsPaused = FALSE;
	}

	int iCurrInstr = g_Script.InstrStream.iCurrInstr;
	int iOpcode = g_Script.InstrStream.pInstrs [ iCurrInstr ].iOpcode;

	printf ( "\t" );
	if ( iOpcode < 10 )
		printf ( " %d", iOpcode );
	else
		printf ( "%d", iOpcode );
	printf ( " %s ", ppstrMnemonics [ iOpcode ] );

	switch ( iOpcode ){
		case INSTR_MOV:
		case INSTR_ADD:
		case INSTR_SUB:
		case INSTR_MUL:
		case INSTR_DIV:
		case INSTR_MOD:
		case INSTR_EXP:
		case INSTR_AND:
		case INSTR_OR:
		case INSTR_XOR:
		case INSTR_SHL:
		case INSTR_SHR:
		{
			Op Dest = ResolveOp ( 0 );
			Op Source = ResolveOp ( 1 );

			switch ( iOpcode ){
				case INSTR_MOV:
					if ( ResolveOpPntr ( 0 ) == ResolveOpPntr ( 1 ) )
						break;
					CopyOp( & Dest, Source );
					break;

				case INSTR_ADD:
					if ( Dest.iType == OP_TYPE_INT )
						Dest.iValue += ResolveOpAsInt ( 1 );
					else
						Dest.fValue += ResolveOpAsFloat ( 1 );
					break;

				case INSTR_SUB:
					if ( Dest.iType == OP_TYPE_INT )
						Dest.iValue -= ResolveOpAsInt ( 1 );
                                        else
                                                Dest.fValue -= ResolveOpAsFloat ( 1 );
					break;

				case INSTR_MUL:
					if ( Dest.iType == OP_TYPE_INT )
                                                Dest.iValue *= ResolveOpAsInt ( 1 );
                                        else
                                                Dest.fValue *= ResolveOpAsFloat ( 1 );
                                        break;

				case INSTR_DIV:
                                        if ( Dest.iType == OP_TYPE_INT )
                                                Dest.iValue /= ResolveOpAsInt ( 1 );
                                        else
                                                Dest.fValue /= ResolveOpAsFloat ( 1 );
                                        break;

				case INSTR_MOD:
                                        if ( Dest.iType == OP_TYPE_INT )
                                                Dest.iValue %= ResolveOpAsInt ( 1 );
                                        break;

				case INSTR_EXP:
					if ( Dest.iType == OP_TYPE_INT )
						Dest.iValue = ( int ) pow ( Dest.iValue, ResolveOpAsInt ( 1 ) );
					else
						Dest.fValue = ( float ) pow ( Dest.fValue, ResolveOpAsFloat ( 1 ) );
					break;

				case INSTR_AND:
					if ( Dest.iType == OP_TYPE_INT )
						Dest.iValue &= ResolveOpAsInt ( 1 );
					break;

				case INSTR_OR:
                                        if ( Dest.iType == OP_TYPE_INT )
                                                Dest.iValue |= ResolveOpAsInt ( 1 );
                                        break;

				case INSTR_XOR:
					if ( Dest.iType == OP_TYPE_INT )
						Dest.iValue ^= ResolveOpAsInt ( 1 );
					break;

				case INSTR_SHL:
					if ( Dest.iType == OP_TYPE_INT )
						Dest.iValue <<= ResolveOpAsInt ( 1 );
                                        break;

				case INSTR_SHR:
					if ( Dest.iType == OP_TYPE_INT )
						Dest.iValue >>=  ResolveOpAsInt ( 1 );
					break;
			}

			* ResolveOpPntr ( 0 ) = Dest;

			PrintOpIndir ( 0 );
			printf ( ", " );
			PrintOpValue ( 1 );
			break;
		}

		case INSTR_NEG:
		case INSTR_NOT:
		case INSTR_INC:
		case INSTR_DEC:
		{
			int iDestStoreType = GetOpType ( 0 );
			Op Dest = ResolveOp ( 0 );

			switch ( iOpcode ){
				case INSTR_NEG:
					if ( Dest.iType == OP_TYPE_INT )
						Dest.iValue = - Dest.iValue;
					else
						Dest.fValue = - Dest.fValue;
					break;

				case INSTR_NOT:
					if ( Dest.iType == OP_TYPE_INT )
						Dest.iValue = ~ Dest.iValue;
					break;

				case INSTR_INC:
					if ( Dest.iType == OP_TYPE_INT )
						++ Dest.iValue;
					else
						++ Dest.fValue;
					break;

				case INSTR_DEC:
					if ( Dest.iType == OP_TYPE_INT )
						-- Dest.iValue;
					else
						-- Dest.fValue;
					break;
			}

			* ResolveOpPntr ( 0 ) = Dest;
			PrintOpIndir ( 0 );
			break;
		}

		case INSTR_CONCAT:{
			Op Dest = ResolveOp ( 0 );
			char * pstrSourceString = ResolveOpAsString ( 1 );
			if ( Dest.iType != OP_TYPE_STRING )
				break;

			int iNewStringLength = strlen ( Dest.pValue ) + strlen ( pstrSourceString );
			char * pstrNewString = ( char * ) malloc ( iNewStringLength + 1 );
			strcpy ( pstrNewString, Dest.pValue );
			strcat ( pstrNewString, pstrSourceString );
			free ( Dest.pValue);
			Dest.pValue = pstrNewString;

			* ResolveOpPntr ( 0 ) = Dest;

			PrintOpIndir ( 0 );
			printf ( ", " );
			PrintOpValue ( 1 );
			break;
		}

		case INSTR_GETCHAR:{
			Op Dest = ResolveOp ( 0 );
			char * pstrSourceString = ResolveOpAsString ( 1 );
			char * pstrNewString;
			if ( Dest.iType == OP_TYPE_STRING ){
				if ( strlen ( Dest.pValue ) >= 1 ){
					pstrNewString = Dest.pValue;
				}else{
					free ( Dest.pValue );
					pstrNewString = ( char * ) malloc ( 2 );
				}
			}else{
				pstrNewString = ( char * ) malloc ( 2 );
				Dest.iType = OP_TYPE_STRING;
			}

			int iSourceIndex = ResolveOpAsInt ( 2 );
			pstrNewString [ 0 ] = pstrSourceString [ iSourceIndex ];
			pstrNewString [ 1 ] = '\0';

			Dest.pValue = pstrNewString;

			PrintOpIndir ( 0 );
			printf ( ", " );
			PrintOpValue ( 1 );
			printf ( ", " );
			PrintOpValue ( 2 );
			break;
		}

		case INSTR_SETCHAR:{
			int iDestIndex = ResolveOpAsInt ( 1 );
			if ( ResolveOpType ( 0 ) != OP_TYPE_STRING )
				break;

			char * pstrSourceString = ResolveOpAsString ( 2 );
			ResolveOpPntr ( 0 )->pValue[ iDestIndex ] = pstrSourceString [ 0 ];

			PrintOpIndir ( 0 );
                        printf ( ", " );
                        PrintOpValue ( 1 );
                        printf ( ", " );
                        PrintOpValue ( 2 );
                        break;
		}

		case INSTR_JMP:{
			Op targetOp = ResolveOp ( 0 );
			int iTargetIndex = targetOp.iValue;
			PrintOpValue ( 0 );//先打印 否则iCurrInstr改变 PrintOpValue（0）取的第0个操作数不对
			g_Script.InstrStream.iCurrInstr = iTargetIndex;
			break;
		}

		case INSTR_JE:
		case INSTR_JNE:
		case INSTR_JG:
		case INSTR_JL:
		case INSTR_JGE:
		case INSTR_JLE:
		{
			Op Op0 = ResolveOp ( 0 );
			Op Op1 = ResolveOp ( 1 );
			Op Op2 = ResolveOp ( 2 );
			int iTargetIndex = Op2.iValue;

			int iJump = FALSE;
			switch ( iOpcode ){
				case INSTR_JE:{
					switch ( Op0.iType ){
						case OP_TYPE_INT:
							if ( Op0.iValue == Op1.iValue )
								iJump = TRUE;
							break;
						case OP_TYPE_FLOAT:
							if ( Op0.fValue == Op1.fValue )
                                                                iJump = TRUE;
                                                        break;
						case OP_TYPE_STRING:
							if ( strcmp ( Op0.pValue, Op1.pValue ) == 0 )
								iJump = TRUE;
							break;
					}
					break;
				}

				case INSTR_JNE:{
                                        switch ( Op0.iType ){
                                                case OP_TYPE_INT:
                                                        if ( Op0.iValue != Op1.iValue )
                                                                iJump = TRUE;
                                                        break;
                                                case OP_TYPE_FLOAT:
                                                        if ( Op0.fValue != Op1.fValue )
                                                                iJump = TRUE;
                                                        break;
                                                case OP_TYPE_STRING:
                                                        if ( strcmp ( Op0.pValue, Op1.pValue ) != 0 ) 
                                                                iJump = TRUE;
                                                        break;
                                        }
					break;
                                }

				case INSTR_JG:{
					if ( Op0.iType == OP_TYPE_INT ){
						if ( Op0.iValue > Op1.iValue )
							iJump = TRUE;
					}else{
						if ( Op0.fValue > Op1.fValue )
							iJump = TRUE;
					}
					break;
				}

				case INSTR_JL:{
					if ( Op0.iType == OP_TYPE_INT ){
                                                if ( Op0.iValue < Op1.iValue )
                                                        iJump = TRUE;
                                        }else{
                                                if ( Op0.fValue < Op1.fValue )
                                                        iJump = TRUE;
                                        }
                                        break;
				}

				case INSTR_JGE:{
					if ( Op0.iType == OP_TYPE_INT ){
                                                if ( Op0.iValue >= Op1.iValue )
                                                        iJump = TRUE;
                                        }else{
                                                if ( Op0.fValue >= Op1.fValue )
                                                        iJump = TRUE;
                                        }
                                        break;
				}

				case INSTR_JLE:{
                                        if ( Op0.iType == OP_TYPE_INT ){
                                                if ( Op0.iValue <= Op1.iValue )
                                                        iJump = TRUE;
                                        }else{
                                                if ( Op0.fValue <= Op1.fValue )
                                                        iJump = TRUE;
                                        }
                                        break;
                                }
			}

			PrintOpValue ( 0 );
			printf ( ", " );
			PrintOpValue ( 1 );
			printf ( ", " );
			PrintOpValue ( 2 );
			printf ( " " );

			if ( iJump ){
				g_Script.InstrStream.iCurrInstr = iTargetIndex;
				printf ( "(True)" );
			}else{
				printf ( "(False)" );
			}

			break;
		}

		case INSTR_PUSH:{
			Op Source = ResolveOp ( 0 );
			Push ( Source );

			PrintOpValue ( 0 );
			break;
		}

		case INSTR_POP:{
			* ResolveOpPntr ( 0 ) = Pop ();

			PrintOpIndir ( 0 );
			break;
		}

		case INSTR_CALL:{
			int oldStackFrameIndex = g_Script.Stack.iFrameIndex;

			int iFuncIndex = ResolveOpAsInt ( 0 );
			FuncNode Dest = GetFunc ( iFuncIndex );

			Op ReturnAddr;
			ReturnAddr.iValue = g_Script.InstrStream.iCurrInstr + 1;
			Push ( ReturnAddr );

			PushFrame ( Dest.iLocalDataSize + 1 );

			Op FuncIndex;
			FuncIndex.iValue = iFuncIndex;
			FuncIndex.iOffsetIndex = oldStackFrameIndex;

			SetStackOp ( g_Script.Stack.iTopIndex - 1, FuncIndex );
			g_Script.InstrStream.iCurrInstr = Dest.iEntryPoint;

			printf ( "$$[%d]$$", g_Script.InstrStream.iCurrInstr );
			int iStackFrameSize = Dest.iParamCount + Dest.iLocalDataSize + 1;
			printf ( "%d ( Entry Point: %d, Frame Size: %d )", iFuncIndex, Dest.iEntryPoint, iStackFrameSize );
			break;
		}

		case INSTR_RET:{
			Op FuncIndex  = Pop ();
			FuncNode CurrFunc = GetFunc ( FuncIndex.iValue );
			int iFrameIndex = FuncIndex.iOffsetIndex;

			Op ReturnAddr = GetStackOp ( g_Script.Stack.iTopIndex - ( CurrFunc.iLocalDataSize + 1 ) );
			int iStackFrameSize = CurrFunc.iParamCount + CurrFunc.iLocalDataSize + 1;
			PopFrame ( iStackFrameSize );

			g_Script.Stack.iFrameIndex = iFrameIndex;
			g_Script.InstrStream.iCurrInstr = ReturnAddr.iValue;
			printf ( "%d", ReturnAddr.iValue );
			break;

		}

		case INSTR_CALLHOST:
			PrintOpValue ( 0 );
			break;

		case INSTR_PAUSE:
			if ( g_Script.iIsPaused )
				break;

			long iPauseDuration = (long)ResolveOpAsInt ( 0 );
			g_Script.iPauseEndTime = GetCurrTime () + iPauseDuration;
			g_Script.iIsPaused = TRUE;

			PrintOpValue ( 0 );
			break;

		case INSTR_EXIT:{
			Op ExitCode = ResolveOp ( 0 );
			int iExitCode = ExitCode.iValue;
			iExitExecLoop = TRUE;

			PrintOpValue ( 0 );
			break;
		}
	}

	printf ( "\n" );

	if ( iCurrInstr == g_Script.InstrStream.iCurrInstr )
		++ g_Script.InstrStream.iCurrInstr;

	if ( iExitExecLoop )
		break ;
	}
}
